package topics

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"strconv"

	kitlog "github.com/go-kit/kit/log"
	"github.com/go-kit/kit/transport"
	kithttp "github.com/go-kit/kit/transport/http"
	"github.com/gorilla/mux"
)

// MakeHandler是专题相关接口的httpHandler，用于在主程序中进行路由注册
func MakeHandler(s Service, sts SmartTopicsService, logger kitlog.Logger) http.Handler {
	opts := []kithttp.ServerOption{
		// ServerErrorHandler是kit在服务器异常时的调用的处理接口，这里使用NewLogErrorHandler仅将错误进行打印
		kithttp.ServerErrorHandler(transport.NewLogErrorHandler(logger)),
		// ServerErrorEncoder是kit在服务异常时将错误编码的处理接口，这里使用自定义errorEncoder，将错误编码为error: err.Error()的字典
		// 对业务代码抛出的代码进行的处理(endpoint返回的error)
		kithttp.ServerErrorEncoder(errorEncoder),
	}

	// getSmartTopicsHandler是获取分页智能专题接口的handler
	getSmartTopicsHandler := kithttp.NewServer(
		makeGetSmartTopicsEndpoint(sts),
		decodeGetSmartTopicsRequest,
		encodeResponse,
		opts...,
	)

	// getKeywordTopicsHandler是获取分页关键字分层专题接口的handler
	getKeywordTopicsHandler := kithttp.NewServer(
		makeGetKeywordTopicsEndpoint(s),
		decodeGetKeywordTopicsRequest,
		encodeResponse,
		opts...,
	)

	// delOrEnabledKeywordTopicHandler是关键字专题删除或者启用开关的handler
	delOrEnabledKeywordTopicHandler := kithttp.NewServer(
		makeDeleteKeywordTopicEndpoint(s),
		decodeDelOrEnabledKeywordTopic,
		encodeResponse,
		opts...,
	)

	// updateKeywordTopicHandler是关键字专题修改的handler
	updateKeywordTopicHandler := kithttp.NewServer(
		makeUpdateKeywordTopicEndpoint(s),
		decodeUpdateKeywordTopic,
		encodeResponse,
		opts...,
	)

	// createKeywordTopicHandler是关键字专题创建的handler
	createKeywordTopicHandler := kithttp.NewServer(
		makeCreateKeywordTopicEndpoint(s),
		decodeCreateKeywordTopic,
		encodeResponse,
		opts...,
	)

	// PUT			/api/v1/topics/keyword					修改关键字专题
	// POST    		/api/v1/topics/keyword                  创建关键字专题
	// DELETE    	/api/v1/topics/keyword				  	删除单个关键字专题和单个关键字专题的开关
	// GET			/api/v1/topics/smart					获取全量智能专题
	// GET			/api/v1/topics/keyword					获取全量关键字专题

	// 创建新路由实例，并将从主函数接受的路由分发给不同的handler
	r := mux.NewRouter()
	r.Methods("POST").Path(`/api/v1/topics/keyword`).Handler(createKeywordTopicHandler)
	r.Methods("PUT").Path(`/api/v1/topics/keyword`).Handler(updateKeywordTopicHandler)
	r.Methods("DELETE").Path(`/api/v1/topics/keyword`).Handler(delOrEnabledKeywordTopicHandler)
	r.Methods("GET").Path(`/api/v1/topics/smart`).Handler(getSmartTopicsHandler)
	r.Methods("GET").Path(`/api/v1/topics/keyword`).Handler(getKeywordTopicsHandler)
	return r
}

// decodeGetSmartTopicsRequest是将http请求报文进行参数检查并序列化为getSmartTopicsRequest的函数
func decodeGetSmartTopicsRequest(_ context.Context, r *http.Request) (interface{}, error) {
	// 这里对请求体和请求参数进行解析，之后可以在r.Form中直接获取
	err := r.ParseForm()
	if err != nil {
		return nil, err
	}
	page := r.Form.Get("page")
	if page == "" {
		return nil, errors.New("需要page参数")
	}
	// 需要将page字符串转化为整数
	pageInt, err := strconv.Atoi(page)
	if err != nil {
		return nil, err
	}
	// 默认page从1开始
	if pageInt < 1 {
		return nil, errors.New("page不能小于1")
	}
	size := r.Form.Get("size")
	// 将size字符串转化为整数
	sizeInt, err := strconv.Atoi(size)
	if err != nil {
		return nil, err
	}
	if sizeInt <= 0 {
		return nil, errors.New("page不能小于1")
	}
	// 返回请求参数的序列化结果给endpoint使用
	return getSmartTopicsRequest{Page: pageInt, Size: sizeInt}, nil
}

// decodeGetKeywordTopicsRequest是将http请求报文进行参数检查并序列化为getKeywordTopicsRequest的函数
func decodeGetKeywordTopicsRequest(_ context.Context, r *http.Request) (interface{}, error) {
	// 这里对请求参数和请求体进行解析
	err := r.ParseForm()
	if err != nil {
		return nil, err
	}
	page := r.Form.Get("page")
	if page == "" {
		return nil, errors.New("需要page参数")
	}
	// 将page参数转换为整数并对格式进行检查
	pageInt, err := strconv.Atoi(page)
	if err != nil {
		return nil, err
	}
	// 默认page从1开始
	if pageInt < 1 {
		return nil, errors.New("page不能小于1")
	}
	size := r.Form.Get("size")
	// 将size参数转换为整数并对格式进行检查
	sizeInt, err := strconv.Atoi(size)
	if err != nil {
		return nil, err
	}
	if sizeInt <= 0 {
		return nil, errors.New("page不能小于1")
	}
	return getKeywordTopicsRequest{Page: pageInt, Size: sizeInt}, nil
}

// decodeCreateKeywordTopic是将http请求报文进行参数检查并序列化为createKeywordTopicRequest的函数
func decodeCreateKeywordTopic(_ context.Context, r *http.Request) (interface{}, error) {
	var request createKeywordTopicRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		return nil, err
	}
	// 对创建自定义专题的必要参数进行检查
	if request.Name == "" {
		return nil, errors.New("没有名字")
	}
	if len(request.Keywords) == 0 {
		return nil, errors.New("没有关键词")
	}
	if request.BeginTimestamp == nil || request.EndTimestamp == nil {
		return nil, errors.New("没有开始或者结束时间")
	}
	return request, nil
}

// decodeDelOrEnabledKeywordTopic是将http请求报文进行参数检查并序列化为deleteKeywordTopicRequest或switchEnabledKeywordTopicRequest的函数
func decodeDelOrEnabledKeywordTopic(_ context.Context, r *http.Request) (interface{}, error) {
	// 这里对请求参数和请求体进行解析
	err := r.ParseForm()
	if err != nil {
		return nil, err
	}
	// 对必要的id进行检查
	id := r.Form.Get("id")
	if id == "" {
		return nil, errors.New("需要id")
	}
	enabled := r.Form.Get("enabled")
	// 根据是否携带enabled参数而将请求序列化为不同的request
	if enabled == "" {
		return deleteKeywordTopicRequest{Id: id}, nil
	} else {
		// 要求enabled传入的参数必须为1或者0
		enabledInt, err := strconv.Atoi(enabled)
		if err != nil {
			return nil, err
		}
		if enabledInt != 0 && enabledInt != 1 {
			return nil, errors.New("enabled只能为1和0")
		}
		if enabledInt == 0 {
			return switchEnabledKeywordTopicRequest{Id: id, Enabled: false}, nil
		}
		return switchEnabledKeywordTopicRequest{Id: id, Enabled: true}, nil
	}
}

// decodeUpdateKeywordTopic是将http请求序列化为updateKeywordTopicRequest的函数
func decodeUpdateKeywordTopic(_ context.Context, r *http.Request) (interface{}, error) {
	var request updateKeywordTopicRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		return nil, err
	}
	// 检查更新关键字专题的必要参数
	if request.CreateTimestamp == 0 {
		return nil, errors.New("需要createTimestamp")
	}
	if request.Id == "" {
		return nil, errors.New("需要id")
	}
	if request.Name == "" {
		return nil, errors.New("需要名字")
	}
	return request, nil
}

// encodeResponse为通用的encode方法，将response序列化为json字符串
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(response)
}

// errorEncoder是
func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	_ = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
}
